package com.ciaojian.client.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ciaojian.client.service.*;
import com.ciaojian.client.util.Constants;
import com.ciaojian.core.base.annotation.ApiIdempotent;
import com.ciaojian.core.bytedance.ByteDanceMiniConfig;
import com.ciaojian.core.bytedance.DySubscribeApi;
import com.ciaojian.core.bytedance.MiniAppRequestApi;
import com.ciaojian.core.bytedance.api.ByteDanceRequestApi;
import com.ciaojian.core.bytedance.vo.ByteMiniAccessToken;
import com.ciaojian.core.constant.RedisConstant;
import com.ciaojian.core.enums.business.OrderOrderTypeEnum;
import com.ciaojian.core.enums.business.OrderStatusEnum;
import com.ciaojian.core.enums.business.WorkerWorkerStatusEnum;
import com.ciaojian.core.exception.EntityNotFoundException;
import com.ciaojian.core.mapper.OrderMapper;
import com.ciaojian.core.model.*;
import com.ciaojian.core.model.dy.DyOrder;
import com.ciaojian.core.model.vo.OrderVo;
import com.ciaojian.core.model.vo.WorkerVo;
import com.ciaojian.core.util.JsonUtil;
import com.ciaojian.core.util.LogUtil;
import com.ciaojian.core.util.RedisUtil;
import com.ciaojian.core.util.Result;
import com.ciaojian.miniapp.api.WxMiniAppApi;
import com.ciaojian.miniapp.model.request.submessage.BaseTemplate;
import com.ciaojian.miniapp.model.request.submessage.TakeNumMessage;
import com.ciaojian.miniapp.model.request.submessage.TemplateIdEnum;
import com.ciaojian.miniapp.model.response.SubMessageSendResponse;
import com.ciaojian.miniapp.model.response.UrlLinkGenerateResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author Atlakyn
 */
@Api(tags = "订单模块")
@RestController
@RequestMapping("/order")
@Slf4j
public class OrderController {
    @Resource
    private OrderServiceImpl orderService;
    @Resource
    private WorkerServiceImpl workerService;
    @Resource
    private StoreServiceImpl storeService;
    @Resource
    private MemberServiceImpl memberService;
    @Resource
    private RedisTemplate<String, Integer> redisTemplate;
    @Resource
    private OrderCommentServiceImpl orderCommentService;
//    @Resource
//    private ByteDanceService byteDanceService;
    @Resource
    private CouponCodeServiceImpl couponCodeService;

    @GetMapping("/genUrlLink")
    public Result genUrlLink() throws JsonProcessingException {
        return Result.ok(WxMiniAppApi.urlLinkGenerate(UrlLinkGenerateResponse.class));
    }

    @ApiOperation("根据订单ID查询评价")
    @GetMapping("/{orderId}/comment")
    public Result getByOrderId(@PathVariable Integer orderId) {
        return Result.ok(orderCommentService.getBaseMapper().getByOrderId(orderId));
    }

    @ApiOperation("查询自动取消的订单")
    @GetMapping("/auto-cancel")
    public Result queryAutoCancelOrder(@NotNull(message = "用户Id不能为空") Integer userId) {
        Set<Integer> orderSet = redisTemplate.opsForSet().members(RedisConstant.AUTO_CANCEL_ORDER + userId);
        List<Order> list = new ArrayList<>();
        if (orderSet != null) {
            orderSet.forEach(orderId -> {
                Order order = new Order().selectById(orderId);
                list.add(order);
            });
        }
        // 查询成功 删除 Redis 中的缓存
        redisTemplate.delete(RedisConstant.AUTO_CANCEL_ORDER + userId);
        return Result.ok(list);
    }

    @ApiOperation("取消订单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "orderId", value = "订单号", paramType = "form"),
            @ApiImplicitParam(name = "reason", value = "取消原因", paramType = "form")
    })
    @PutMapping("/cancel")
    public Result cancelNum(@NotNull(message = "请选择要取消的订单") Integer orderId,
                            @NotNull(message = "请选择取消原因") String reason) {
        Order order = orderService.getById(orderId);
        OrderStatusEnum status = order.getStatus();
        Assert.isTrue(status.equals(OrderStatusEnum.waiting), "非等待服务无法取消");

        order.setStatus(OrderStatusEnum.cancel);
        order.setCancelReason(reason);
        if (order.updateById()) {
            return Result.ok("取消成功");
        } else {
            return Result.error("取消失败");
        }
    }

    @ApiOperation("排队进度查询")
    @GetMapping("/queue-progress")
    public Result queueProgress(Integer workerId) {
        return Result.ok(orderService.findQueueByWorkerId(workerId));
    }



    /**
     * 计算订单优惠价格
     *
     * @param orderId          订单 id
     * @param type             服务类型
     * @param code             优惠券码
     * @param memberDiscountId 首单优惠 id
     * @return 计算后的价格
     */
    @ApiOperation("计算订单优惠价格")
    @GetMapping("/cal-amount/{orderId}")
    public Result calAmount(@PathVariable String orderId,
                            String type,
                            @ApiParam("优惠券码") String code,
                            @ApiParam("首单优惠id") Integer memberDiscountId) throws JsonProcessingException {
        // 优惠后的价格
        // 根据 id 查订单
        QueryWrapper<Order> queryOrder = new QueryWrapper<>();
        queryOrder.eq("id", orderId);
        Order order = orderService.getOne(queryOrder);
        Assert.notNull(order, "订单不存在");

        BigDecimal amount = orderService.calDiscount(
                order,
                code,
                type,
                memberDiscountId,
                order.getGoodsTotal(),
                order.getUserId());
        return Result.ok(amount);
    }

    /**
     * 计算订单优惠价格
     *
     * @param orderId          订单 id
     * @param type             服务类型
     * @param codes            优惠券码
     * @param memberDiscountId 首单优惠 id
     * @return 计算后的价格
     */
    @ApiOperation("计算订单优惠价格")
    @GetMapping("/cal-amount/{orderId}/v2")
    public Result calAmountV2(@PathVariable String orderId,
                              String type,
                              @ApiParam("优惠券码") String[] codes,
                              @ApiParam("首单优惠id") Integer memberDiscountId) throws JsonProcessingException {
        // 优惠后的价格
        // 根据 id 查订单
        QueryWrapper<Order> queryOrder = new QueryWrapper<>();
        queryOrder.eq("id", orderId);
        Order order = orderService.getOne(queryOrder);
        Assert.notNull(order, "订单不存在");

        BigDecimal amount = orderService.calDiscountV2(
                order,
                Arrays.asList(codes),
                type,
                memberDiscountId,
                order.getGoodsTotal(),
                order.getUserId());
        return Result.ok(amount);
    }

    @ApiOperation("免单接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "orderId", value = "订单id", required = true),
            @ApiImplicitParam(name = "type", value = "服务类型", required = true),
            @ApiImplicitParam(name = "code", value = "券码"),
            @ApiImplicitParam(name = "memberDiscountId", value = "首单优惠id")
    })
    @ApiIdempotent
    @Transactional(rollbackFor = Exception.class)
    @PostMapping("/free-order")
    public Result freeOrder(@RequestParam String orderId,
                            @RequestParam String type,
                            String[] codes,
                            Integer memberDiscountId) throws JsonProcessingException {
        if (StrUtil.isEmpty(orderId)) {
            return Result.error("订单id不能为空");
        }
        if (StrUtil.isEmpty(type)) {
            return Result.error("服务类型不能为空");
        }

        // 根据 id 查订单
        QueryWrapper<Order> queryOrder = new QueryWrapper<>();
        queryOrder.eq("id", orderId);
        Order order = orderService.getBaseMapper().selectOne(queryOrder);

        try {
            String key = RedisUtil.getKey("metro::user::" + order.getUserId());
            if (ObjectUtil.isNotNull(key)) {
                Store store = new Store().selectById(order.getStoreId());
                store.setQrHair(store.getQrHair()+1);
                store.updateById();
            }
        }catch (Exception e){}

        // 校验订单状态
        Assert.isTrue(order.getStatus().equals(OrderStatusEnum.wait_pay)
                || order.getStatus().equals(OrderStatusEnum.servicing)
                || order.getStatus().equals(OrderStatusEnum.sling), "订单状态不是待付款");

        orderService.freeOrder(order, Arrays.asList(codes), type, memberDiscountId);

        try{
            if (order.getCouponId()==469) {
                QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
                orderQueryWrapper.eq("user_id",order.getUserId());
                orderQueryWrapper.eq("worker_id",order.getWorkerId());
                orderQueryWrapper.eq("pay_type","抖音审核");
                Order order1 = new Order().selectOne(orderQueryWrapper);
                if (ObjectUtil.isNull(order1)) {
                    ProductGroup productGroup = new ProductGroup().selectById(10);
                    couponCodeService.douyinWriteOff(order.getUserId(), order.getWorkerId(), order.getStoreId(), productGroup, order.getYouzanCode(), order.getCouponCode(), order.getCouponId());

                    QueryWrapper<DyOrder> qw = new QueryWrapper<DyOrder>();
                    qw.eq("coupon_code",order.getCouponCode());
                    DyOrder dyOrder = new DyOrder().selectOne(qw);
                    dyOrder.setUpdateTime(new Date());
                    dyOrder.setStatus(5);
                    dyOrder.updateById();
                }
            }
        }catch (Exception e){
            log.info("##免单抖音券请求接口={}", e.getMessage());
        }

        return Result.ok();
    }

    @ApiOperation("我的订单")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "pageNo", value = "页码", defaultValue = "0"),
            @ApiImplicitParam(name = "pageSize", value = "页码数量", defaultValue = "6")
    })
    @GetMapping
    public Result myOrders(Integer userId,
                           @ApiParam("服务类型 0待服务，1待支付，2已完成，3已取消/过号") Integer type,
                           @RequestParam(defaultValue = "0") @ApiParam("订单类型 0全部订单，1剪发订单,2套餐订单") Integer orderType,
                           @RequestParam(defaultValue = "0") Integer pageNo,
                           @RequestParam(defaultValue = "6") Integer pageSize) {
        Assert.notNull(userId, "用户id不能为空");
        Assert.notNull(type, "请选择类型");

        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Order::getUserId, userId)
                .eq(type == 0, Order::getStatus, 0)
                .in(type == 1, Order::getStatus, 1, 2, 7, 21)
                .in(type == 2, Order::getStatus, 3, 5)
                .eq(type == 3, Order::getStatus, -1)
                .in(type == 4, Order::getStatus, 2, 21);
        if (type==2) {
            if (orderType == 0) {
                wrapper.in(Order::getOrderType, OrderOrderTypeEnum.NORMAL, OrderOrderTypeEnum.MEITUAN, OrderOrderTypeEnum.MEMBER_DETAIL);
            } else if (orderType == 1) {
                wrapper.in(Order::getOrderType, OrderOrderTypeEnum.NORMAL, OrderOrderTypeEnum.MEITUAN);
            } else if (orderType == 2) {
                wrapper.in(Order::getOrderType, OrderOrderTypeEnum.MEMBER_DETAIL);
            }
        }else {
            wrapper.in(Order::getOrderType, OrderOrderTypeEnum.NORMAL, OrderOrderTypeEnum.MEITUAN);
        }
        wrapper.orderByDesc(Order::getAddTime);
        IPage<Order> page = orderService.page(new Page<>(pageNo, pageSize), wrapper);
        List<Order> orderList = page.getRecords();
        try {
            if (type == 0) {
                for (Order order : orderList) {
                    Map<String, Integer> waitCensus = orderService.minuteTake(order.getWorkerId(), order.getOrderNo());
                    order.setWaitCensus(waitCensus);
                }
            }
            orderList.forEach(ele -> ele.setMiter(2000));
            return Result.ok(page);
        } catch (Exception e) {
            LogUtil.writeErrorLog(e, e.toString());
            return Result.error("系统异常");
        }
    }

    @ApiOperation("取号")
    @Transactional(rollbackFor = Exception.class)
    @ApiIdempotent
    @PostMapping("/take-num")
    public synchronized Result takeNum(@RequestBody Order order) {
        LogUtil.writeBusinessLog(">>>取号接口参数>>>{order:{}}", order);

        if (order.getUserId() == null || order.getWorkerId() == null) {
            return Result.error("用户ID和发型师ID不能为空");
        }
        // 发型师状态
        WorkerWorkerStatusEnum workerStatus = workerService.getBaseMapper().getWorkerStatusById(order.getWorkerId());
        // 发型师信息
        Worker worker = workerService.getBaseMapper().selectById(order.getWorkerId());

        Assert.isTrue(!workerStatus.equals(WorkerWorkerStatusEnum.NEARLY_DOWNTIME), "亲, 该发型师即将下班暂时无法取号哦");
        Assert.isTrue(!workerStatus.equals(WorkerWorkerStatusEnum.DOWNTIME), "亲, 该发型师正在休息暂时无法取号哦");

        QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status", OrderStatusEnum.wait_pay);
        queryWrapper.eq("user_id", order.getUserId());
        Order order1 = orderService.getOne(queryWrapper);
        Assert.isNull(order1, "亲, 请支付上一笔订单后再进行取号哦");

        //插入记录
        Order newOrder = newOrder(order);
        try {
            // 查询服务对应的字母
            String letter = orderService.getBaseMapper().getLetter(order.getType());
            Assert.notNull(letter, "请选择正确的服务");
            String daysMaxrankNo = orderService.getBaseMapper().getDaysMaxrankNo(order.getStoreId() + "", letter);
            if (daysMaxrankNo == null) {
                newOrder.setRankNo(letter + "101");
            } else {
                daysMaxrankNo = daysMaxrankNo.replace(letter, "");
                newOrder.setRankNo(letter + (Integer.parseInt(daysMaxrankNo) + 1) + "");
            }
            newOrder.setOrderType(OrderOrderTypeEnum.NORMAL);
            newOrder.setStatus(OrderStatusEnum.waiting);
            newOrder.setSendStatus(0);
            newOrder.setOverService(0);
            newOrder.setStoreId(worker.getStoreId());
            newOrder.setStoreName(worker.getStoreName());
            newOrder.setAddTime(new Date());
            try {
                String key = RedisUtil.getKey("metro::user::" + order.getUserId());
                if (ObjectUtil.isNotNull(key)) {
                    newOrder.setRemark("地铁扫码取号");
                }
            }catch (Exception e){}

            orderService.setCustomerType(newOrder);
            orderService.save(newOrder);
        } catch (Exception e) {
            e.printStackTrace();
            LogUtil.writeBusinessErrorLog(e, e.getMessage());
            return Result.error("取号失败！");
        }
        return Result.ok(newOrder.getOrderNo());
    }

    /**
     * 以前的老代码
     *
     * @param order
     * @return
     */
    private Order newOrder(Order order) {
        Date date = new Date();
        //worker_id,user_id,store_id,worker_name,store_name,type
        order.setUserId(order.getUserId());
        order.setWorkerId(order.getWorkerId());
        order.setStoreId(order.getStoreId());
        order.setWorkerName(order.getWorkerName());
        order.setStoreName(order.getStoreName());
        order.setType(order.getType());
        order.setGoodsTotal(order.getGoodsTotal());
        order.setTotal(order.getGoodsTotal());

        //rank_no
        int i = 1;
        String num;
        String rankNo;
        if (Constants.DAY2 != null && !Constants.DAY2.equals(Constants.DAY)) {
            //日期变了
            i = 1;
            num = "0" + i;
        } else { //还是当天
            if (i < 10) {
                num = "0" + i;
            } else {
                num = "" + i;
            }
        }
        i++;
        rankNo = Constants.DAY + num;
        order.setRankNo(rankNo);
        Constants.DAY2 = Constants.DAY;

        //订单号 order_no
        order.setOrderNo(orderService.generateOrderNoByUserId(order.getUserId()));
        //about_rank_time
        order.setAboutRankTime(date);

        return order;
    }

    @ApiOperation("取号成功,此接口需要 apiToken")
    @ApiIdempotent
    @PostMapping("/take-success")
    public Result takeSuccess(String orderNo, String openId) throws IOException {
        Assert.hasText(openId, "openid不能为空");
        Assert.hasText(orderNo, "订单号不能为空");

        OrderMapper orderMapper = orderService.getBaseMapper();
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("order_no", orderNo);
        Order order = orderService.getOne(wrapper);
        //需等待人数和时间
        Map<String, Integer> map = orderService.minuteTake(order.getWorkerId(), order.getOrderNo());
        order.setWaitCensus(map);
        Order orderData = orderMapper.getOrderData(orderNo);
        Integer storeId = orderMapper.getOrderStoreId(orderNo);
        Map<String, Integer> waitCensus = order.getWaitCensus();
        String rankNokey = RedisUtil.getKey("takeSuccessPush:" + storeId + ":" + orderData.getRankNo());
        // 发型师手机号
        String phone = workerService.getBaseMapper().getWorkerPhoneById(order.getWorkerId());
        // 取号通知
        if (rankNokey == null) {
            if (order.getChannel()==1) {
                BaseTemplate baseTemplate = BaseTemplate.builder(TemplateIdEnum.TAKE_NUM_MESSAGE)
                        .touser(openId)
                        .data(TakeNumMessage.builder()
                                .thing1("建议提前到店，叫号超3分钟排队将延后处理")
                                .thing2(orderData.getStoreName())
                                .character_string3(order.getRankNo())
                                .thing4(String.valueOf(waitCensus.get("waitNum")).concat("人"))
                                .name5(order.getWorkerName())
                                .build()
                        );
                try {
                    SubMessageSendResponse response = WxMiniAppApi.subscribeMessageSend(baseTemplate);
                    if (response.getErrcode() == 0) {
                        RedisUtil.setKeyTime("takeSuccessPush:" + storeId + ":" + orderData.getRankNo(), 86400, orderData.getRankNo());
                    } else {
                        LogUtil.writeBusinessLog("取号通知失败: {}", response.getErrmsg());
                    }
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                    LogUtil.writeErrorLog(e.getMessage());
                }
            }
            if(order.getChannel()==2){
                JSONObject param1 = new JSONObject();
                param1.put("appid", ByteDanceMiniConfig.APP_ID);
                param1.put("secret", ByteDanceMiniConfig.SECRET_ID);
                param1.put("grant_type", "client_credential");
                //定义接收数据
                JSONObject result = new JSONObject();

                String access_token_v2 = ByteDanceRequestApi.sendRequest(MiniAppRequestApi.ACCESS_TOKEN_V2, param1);

                result = JSON.parseObject(access_token_v2);
                ByteMiniAccessToken byteMiniAccessToken = result.toJavaObject(ByteMiniAccessToken.class);

                JSONObject param = new JSONObject();
                param.put("access_token", byteMiniAccessToken.getData().getAccess_token());
                param.put("app_id", ByteDanceMiniConfig.APP_ID);
                param.put("tpl_id", ByteDanceMiniConfig.QUEUE_INFORM);
                param.put("open_id", openId);
                String jsonStr = "{ \"门店\": \"" + orderData.getStoreName() + "\", " +
                        "\"排队号\": \"" + order.getRankNo() + "\"," +
                        "\"等待人数\": \""+String.valueOf(waitCensus.get("waitNum")).concat("人")+"\"," +
                        "\"发型师\": \"" + order.getWorkerName() + "\"," +
                        "\"温馨提示\": \"建议提前到店，叫号超3分钟排队将延后处理\" }";
                param.put("data",  JsonUtil.json2Map(jsonStr));

                //定义接收数据
                HttpResponse response = DySubscribeApi.subscribeMessageSend(param, ByteDanceMiniConfig.SUBSCRIBE_NOTIFICATION, "/api/apps/subscribe_notification/developer/v1/notify");

                if (response.getStatusLine().getStatusCode() == 200) {
                    param = JSON.parseObject(EntityUtils.toString(response.getEntity(), "UTF-8"));
                }else {
                    LogUtil.writeBusinessErrorLog("发型师提醒用户排队失败,抖音返回信息: {}", JSON.parseObject(EntityUtils.toString(response.getEntity(), "UTF-8")));
                }
            }
        }
        Map<String, Object> response = BeanUtil.beanToMap(order);
        response.put("phone", phone);
        return Result.ok(response);
    }


    @ApiOperation("排队列表")
    @GetMapping("/orderList")
    public Result getOrderList(Integer id) {
        List<OrderVo> dOrderVo = orderService.getOrderVo(id);
        for (OrderVo orderVo : dOrderVo) {
            //        获取当前发型师下有多少人在排队，需要等待多少分钟
            Integer workerId = orderVo.getWorkerId();
            WorkerVo workerVo = workerService.getVoById(workerId);
            Map<String, Integer> map = orderService.minuteTakes(workerVo.getWid(), orderVo.getOrderNo());
            orderVo.setWaitMessage(map);
            //        获取店铺
            Integer storeId = orderVo.getStoreId();
            Store store = storeService.getById(storeId);
            String title = store.getTitle();
            String address = store.getAddress();
            orderVo.setTitle(title);
            orderVo.setAddress(address);
            orderVo.setLevel(workerVo.getLevelId());
            orderVo.setAvatar(workerVo.getAvatar());
            orderVo.setName(workerVo.getName());

        }
        return Result.ok(dOrderVo);
    }


    @ApiOperation("查询服务优惠")
    @ApiImplicitParams(@ApiImplicitParam(value = "serviceId", name = "服务Id"))
    @ApiResponses(@ApiResponse(code = 200, message = "success", response = Map.class))
    @GetMapping("/service-discount")
    public Result getServiceDiscount(Integer serviceId) {
        HaircutService haircutService = new HaircutService().selectById(serviceId);
        Assert.notNull(haircutService, "服务不存在");
        Map<String, String> memberDiscount = memberService.getMemberDiscount(haircutService.getTitle());
        return Result.ok(memberDiscount);
    }


    @ApiOperation("商城订单")
    @GetMapping("/shoppingOrder")
    public Result getShoppingOrder(@RequestParam("userId") Integer userId) {
        return Result.ok( orderService.getShoppingOrder(userId));
    }


    @ApiOperation("商城订单详情")
    @GetMapping("/shoppingOrder/{orderId}")
    public Result getShoppingOrder(
                            @PathVariable Integer orderId,
                            @RequestParam("userId") Integer userId) {

        return orderService.getShoppingOrderInfo(orderId,userId);
    }

    @ApiOperation("用户剪发")
    @GetMapping("/hairperiod")
    public Result hairperiod(@RequestParam Integer userId, @RequestParam @DateTimeFormat(pattern = "yyyy-MM") Date month) throws ParseException {

        return orderService.getHairperiod(userId,month);
    }
}
